<?php

// Place in ./app/controllers/feeds_controller.php

class FeedsController extends AppController {
  var $error = '';

  function add() {
    if (!empty($this->data)) {
      if ($this->Feed->save($this->data)) {
        $this->Session->setFlash('Added');
        $this->redirect(array('action'=>'index'));
      } else {
        $this->Session->setFlash('There was a problem with your section element');
      }
    }
  } // add()

  function delete($id = NULL) {
		if (isset($id) && $id != null) {
			$this->Feed->delete($id);
			$this->Session->setFlash("Deleted");
			$this->redirect(array('action'=>'index'));
		}
  } // delete()
	
	function since($last_update = null) {
		if ($last_update == null) { return; }
		
		$this->layout = null;
		
		if ($this->_loggedIn()) {
			print "Logged In";
		} else {
			print "Not Logged In";
		}
		
	} // since()
	
	function status() {
		$this->layout = null;
		$this->header('Content-Type: application/json'); 
		
		$result = $this->Feed->find('all', array(
			'conditions' => array(
				'Feedfolder.user_id' => $this->Auth->user('id')
			),
			'fields' => array('Feed.id','(SELECT Count(1) FROM entries AS Entry WHERE Entry.feed_id = Feed.id) AS unread','Feed.feedfolder_id as feedfolder_id'),
			'group' => 'Feed.id',
			'recursive' => 0
		));
		
		$unread_list = array();
		
		foreach ($result as $idx => $res) {
			$feed_entry = new stdClass();
			
			$feed_entry->id = $res['Feed']['id'];
			$feed_entry->feedfolder_id = $res['Feed']['feedfolder_id'];
			$feed_entry->unread = $res[0]['unread'];
			
			if ($feed_entry->unread > 0) {
			  $unread_list[] = $feed_entry;
			}
		} 

		$this->set('result', $unread_list);
		
		$this->render('json');
	} // status()

  function _saveEntry($entry_data = array()) {
		$entry_obj = new Entry();
		
		$exists = $entry_obj->find('all', array('conditions'=>array('guid'=>$entry_data['Entry']['guid'],'feed_id'=>$entry_data['Entry']['feed_id']),'recursive'=>0));
		
		if (count($exists) > 0) {
			$this->error = 'duplicate-entry';
		  return false;
		} else {
			$entry_obj->create();
			if (@$entry_obj->save($entry_data)) {
				return true;
			} else {
				$this->error = 'save-error';
				return false;
			}
		}
	} // _saveEntry()

  function logEvent($event = '(empty)', $type = 'stat') {
		$when = time();
		print "[$type] $when : $event\n";
	} // logEven()

  function update_feeds($force_update = false) {
		if (!defined('CRON_DISPATCHER')) {
			$this->redirect('/');
			exit();
		}
		
		//ini_set('memory_limit', '256M');
		
		$cron_lock = fopen(TMP . 'cron-lock.pid', "w"); 
		if (!flock($cron_lock, LOCK_EX|LOCK_NB)) { 
			$this->logEvent("Cron feed updater already running.  Terminating now.");
			fclose($cron_lock);
			exit(0);
		}
		
		
	  $force_update = $force_update == true ? true : false;
		
		if ($force_update) {
			$this->logEvent('Feed updater is running in FORCED mode');
		}
		
		ini_set('max_execution_time', 0);
		
		$this->layout = null;
		
		$events = array();
		
		
		$opts = array(
			'http'=>array(
				'method'=>"GET",
				'user_agent'=>'Dependence RSS Fetcher/0.1',
				'header'=>"Accept-language: en\r\n" .
									"Cookie: foo=bar\r\n" .
									"Connection: close\r\n",
				'timeout'=>'3'
									
			)
		);
		
		// enable user error handling
		libxml_use_internal_errors(true);
		
		$context = stream_context_create($opts);
		
		$update_started = time();
		
		$cnt = 0;
		
		$user_obj = new User();
		
		$users = $user_obj->find('all', array('recursive'=>0));
		foreach ($users as $user) {
			$user = $user['User'];
			
			$feeds = $this->Feed->find('all', array('conditions'=>array(
																																	'updated <= ' => ($force_update ? 9999999999 : time() - (60 * 60)),
																																	'Feedfolder.user_id' => $user['id'],
																																	'fail_count < ' => 120
																																	),
																							'recursive' => 0
																							
																						 ));
			
			$fd_cnt = count($feeds);
			
			$this->logEvent("Processing $fd_cnt feeds for {$user['username']}");
			
			foreach ($feeds as $feed) {
				$feed_results = array('saved'=>0,'duplicate-entry'=>0,'save-error'=>0);
				
				$feed = $feed['Feed'];
				$valid_feed = true;
				
				if (time() > ($feed['updated'] + (60 * 60)) || $force_update) {
					$this->logEvent("Checking \"" . $feed['title'] . '": ' . $feed['xml']);
					 
					 
					$data = @file_get_contents($feed['xml'], false, $context);
					
					if ($data !== false) {
						
						if (!preg_match('/<(rss|feed|rdf)/is', $data)) { // really dumb "validation"
							$this->logEvent('"' . $feed['title'] . '" does not appear to be RSS or Atom; ignoring.', 'warn');
						} else {
						
							$xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_DTDLOAD);
							if (!$xml) {
								foreach (libxml_get_errors() as $error) {
									//print "[" . $feed['title'] . '] ERROR loading: ' . $feed['xml'] . ', (' . $error->code . ') @ [' . $error->line . ',' . $error->column . '] - ' . $error->message . "\n";
									$this->logEvent('XML Parse Error: ' . $feed['xml'] . ', (' . $error->code . ') @ [l:' . $error->line . ',c:' . $error->column . '] : ' . $error->message, 'eror');
								}
							
								libxml_clear_errors();
							} else { // no XML parse errors
								$entry = new stdClass();
								
								$is_rss = isset($xml->channel);
								
								if (isset($xml->channel)) { // RSS
									$this->logEvent('"' . $feed['title'] . '" is RSS');
									
									foreach ($xml->channel->item as $entry) {
									
										$entry_data = array(
											'Entry' => array(
												'feed_id' => $feed['id'],
												'link' => (string)$entry->link,
												'title' => (string)$entry->title,
												'author' => (isset($entry->author) ? (string)$entry->author : ''),
												'description' => (string)$entry->description,
												'guid' => (isset($entry->guid) && strlen((string)$entry->guid) > 10 ? (string)$entry->guid : sha1((string)$entry->link)),
												'pub_date' => (isset($entry->pubDate) ? strtotime((string)$entry->pubDate) : time())
											)
										);
										
										if ($this->_saveEntry($entry_data)) {
											$feed_results['saved']++;
										} else {
											$feed_results[$this->error]++;
											$this->error = '';
										}
									}
								} elseif (isset($xml->entry)) { // Atom
									$this->logEvent('"' . $feed['title'] . '" is Atom');
									
									
									foreach ($xml->entry as $entry) {
									
										$entry_data = array(
											'Entry' => array(
												'feed_id' => $feed['id'],
												'link' => (isset($entry->link) ? (string)$entry->link->attributes()->href : $entry->id),
												'title' => (string)$entry->title,
												'author' => (isset($entry->author) ? (isset($entry->author->name) ? $entry->author->name : (string)$entry->author) : (isset($xml->author) ? $xml->author->name : '')),
												'description' => (isset($entry->content) ? $entry->content : $entry->summary),
												'guid' => (string)$entry->id,
												'pub_date' => (isset($entry->updated) ? strtotime($entry->updated) : time())
											)
										);
										
										if ($this->_saveEntry($entry_data)) {
											$feed_results['saved']++;
										} else {
											$feed_results[$this->error]++;
											$this->error = '';
										}
										
									}
									
								} else {
									$valid_feed = false;
									$this->logEvent('"' . $feed['title'] . " is neither RSS nor Atom.");
								}
								
								
								$res = '';
								foreach ($feed_results as $key => $cnt) {
									if (strlen($res) > 0) { $res .= ', '; }
									$res .= "$key: $cnt";
								}
								$this->logEvent('"' . $feed['title'] . "\" processing results: " . $res);
								
								if ($valid_feed) {
									$feed_update_data = array(
										'Feed' => array(
											'id' => $feed['id'],
											'fail_count' => 0
										)
									);
									$feed = $this->Feed->save($feed_update_data);
								}
								
								
								
							} // errors?
						} // looks like RSS or Atom
					} else {
						
						$this->logEvent('"' . $feed['title'] . '" could not be retrieved (' . ($feed['fail_count']+1) . '): ' . $feed['xml'], 'warn');
						
						$feed_update_data = array(
							'Feed' => array(
								'id' => $feed['id'],
								'fail_count' => $feed['fail_count'] + 1,
								'updated' => $feed['updated']
							)
						);
						$feed = $this->Feed->save($feed_update_data);
						
					} // feed loaded?
				} else {
					$this->logEvent('"' . $feed['title'] . "\" is current");
				}
			} // foreach feed
		} // foreach user
		
		$update_ended = time();
		$elapsed = $update_ended - $update_started;
		$this->logEvent("Run completed in $elapsed second(s)");
		
		
		$this->set('events', $events);
		
		fclose($cron_lock);
	} // update_feeds()

  function view($id = null, $format = null) {
		$start = microtime(true);
		
		$feed_cache_key = 'feeds-' . $id;
			
		
		if (strtolower($format) == 'json') {
			$this->header('Content-Type: application/json'); 
			$this->layout = null;
		}
			
		//if (($entries = Cache::read($feed_cache_key)) === false) {
			$entries = $this->Feed->Entry->find('all', array('conditions' => array('Entry.feed_id'=>$id,'read'=>0), 'order'=>array('pub_date DESC'), 'limit' => 100, 'recursive' => 0));
			//Cache::write($feed_cache_key, $entries);
		//}
		
		
		
		
		$end = microtime(true);
		
		$this->set('elapsed', $end - $start);
		$this->set('view_id', $id);
		$this->set('feed', $this->Feed->read(null, $id));
		$this->set('entries', $entries);
		$this->set('format', $format);
		
	} // view()

  function pull() {
		
  } // pull()
	
	function subscribe($feed_url = null) {
		
	}
	
	function beforeFilter() {
		parent::beforeFilter();
		
		$this->Auth->allow('update_feeds');
	}
	
	function mark_read($id = null) {
		$this->layout = null;
	
	  
		
		if ($id != null) {
			$id = int($id);
			
			$sql = "
			  UPDATE entries SET read = 1 WHERE feed_id = $id
			";
			
			$this->Feed->query($sql);
			
			$this->set('result', true);
		} else {
			$this->set('result', false);
		}
		
		
		
		$this->render('json');
	} // mark_read()

} // class Feeds

?>